#include "topdbs_connection_rw.hpp"

namespace topdbs
{
    std::mutex  SQLConnectionRw::m_lock; 
    bool SQLConnectionRw::m_status = false;
    SQLConnectionRw* SQLConnectionRw::m_instance = nullptr;
    SQLConnectionRw::AutoRelease SQLConnectionRw::m_release;
    uint16_t SQLConnectionRw::m_current_pos = 0;

    SQLConnectionRw::~SQLConnectionRw()
    {
        //DeleteSQLConnectionRw();
    }    

    int SQLConnectionRw::NewConnectionRW(
        eDBSClient_t dbsClient,
        const string& dbString,
        const string& user ,
        const string& pass ,
        const string& charset,
        bool encrypt,
        eRwType_t rwType )
    {
        std::lock_guard<std::mutex> _lock(SQLConnectionRw::m_lock);
        if(SQLConnectionRw::m_status)
        {
            return ERR_DBS_OK;
        }	
        
        SQLConnection* pConnection = new SQLConnection();
        pConnection->setClientEncode(charset);
        pConnection->setEncrypt(encrypt);

        if( pConnection->connect(dbsClient,dbString,user,pass ) != 0)
        {
            delete pConnection;
            pConnection = nullptr;
            return ERR_DBS_FAIL;
        }
        
        STRwConn* pStConn = new STRwConn();
        pStConn->m_status = ConnSt_Idle;
        pStConn->m_rwtype = rwType;
        pStConn->m_pConnection = pConnection;
        uint64_t nConnectionID = pConnection->getConnectionID();
        m_StConns[nConnectionID] = pStConn;
        dbslog(DBS_DEBUG,"New database Connection succed. connectionID=%lu",nConnectionID);
                
        SQLConnectionRw::m_status = true;
        dbslog(DBS_DEBUG,"Create database Connection rw succed.");
        return ERR_DBS_OK;
    }

    int SQLConnectionRw::DeleteSQLConnectionRw()
    {
        dbslog(DBS_DEBUG,"begin exec delete connection rw size = %d",m_StConns.size());
        for(auto it = m_StConns.begin(); it != m_StConns.end(); )
        {
            auto& pStConn = it->second;
            dbslog(DBS_DEBUG,"Delete database Connection succed. connectionID=%lu",
                pStConn->m_pConnection->getConnectionID());
            delete pStConn;
            pStConn = nullptr;
            m_StConns.erase(it++);
        }
        return ERR_DBS_OK;
    }

    SQLConnection* SQLConnectionRw::GetIdleConnection(const string& sSQL,bool isForce)
    {
        SQLConnection* pConnection = nullptr;
        std::lock_guard<std::mutex> _lock(SQLConnectionRw::m_lock);

        bool isSelect = IsSelectSQL(sSQL);

        if(SQLConnectionRw::m_current_pos + 1 > m_StConns.size())
        {
            SQLConnectionRw::m_current_pos = 0;
        }
        auto iter_begin = m_StConns.begin();
        std::advance(iter_begin,SQLConnectionRw::m_current_pos);   

        for(auto it = iter_begin; it != m_StConns.end(); it++)
        {
            auto& pStConn = it->second;
            if(pStConn->m_status == ConnSt_Idle)
            {
                if(isSelect && !isForce && pStConn->m_rwtype != Conn_Read)
                {
                    continue;
                }  
                if(isSelect && isForce && pStConn->m_rwtype != Conn_Write)
                {
                    continue;
                }  
                if(!isSelect && pStConn->m_rwtype != Conn_Write)
                {
                    continue;
                }            
                pConnection = pStConn->m_pConnection;
                pStConn->m_status = ConnSt_Busy;      
                SQLConnectionRw::m_current_pos++;         
                return pConnection;
            }            
        }
        return pConnection;
    };	    

    int SQLConnectionRw::ReleaseIdleConnection(SQLConnection* pConnection)
    {
        if(!pConnection)
        {
            dbslog(DBS_ERROR,"The input args invalid!!!");
            return ERR_DBS_FAIL;
        }
        std::lock_guard<std::mutex> _lock(SQLConnectionRw::m_lock);
        
        uint64_t nConnectionID = pConnection->getConnectionID();
        auto it = m_StConns.find(nConnectionID);
        if(it == m_StConns.end())
        {
            dbslog(DBS_ERROR,"Not found connection info!!!");
            return ERR_DBS_FAIL;
        }
        auto& pStConn = it->second;
        pStConn->m_status = ConnSt_Idle;
        return ERR_DBS_OK;
    }	

    int SQLConnectionRw::InitEnv( std::vector<DBSConnectionParams>& master_dbsParams,std::vector<DBSConnectionParams>& slave_dbsParams)
    {
        int iRet = 0;
        eRwType_t rwType = Conn_Write;

        SQLConnectionRw* instance = nullptr;
        try
        {
            instance = SQLConnectionRw::getInstance();
            if(!instance)
            {
                dbslog(DBS_ERROR,"getInstance error!!!");
                return ERR_DBS_FAIL;
            }
        }
        catch(const std::exception& ex)
        {
            dbslog(DBS_ERROR,"getInstance exception %s!!!",ex.what());
            return ERR_DBS_FAIL;
        }  

        //create master connection
        for(auto& dbsParams : master_dbsParams)
        {
            rwType = Conn_Write;
            iRet = instance->NewConnectionRW(
                dbsParams.dbsType,
                dbsParams.dbsConnStr,
                dbsParams.dbsUser,
                dbsParams.dbsPass,
                dbsParams.dbsCharset,
                dbsParams.dbsEncrypt,
                rwType
                ); 
            if(iRet != ERR_DBS_OK)
            {
                dbslog(DBS_WARN,"Creat master connection error");
                continue;;
            }	           
        }  

         //create slave connection
        for(auto& dbsParams : slave_dbsParams)
        {
            rwType = Conn_Read;
            iRet = instance->NewConnectionRW(
                dbsParams.dbsType,
                dbsParams.dbsConnStr,
                dbsParams.dbsUser,
                dbsParams.dbsPass,
                dbsParams.dbsCharset,
                dbsParams.dbsEncrypt,
                rwType
                ); 
            if(iRet != ERR_DBS_OK)
            {
                dbslog(DBS_WARN,"Creat master connection error");
                continue;;
            }	           
        }    
        return 0;
    }

    int SQLConnectionRw::InitEnv( const string& dbsfile,const string& dbs_master_name,const string& dbs_slave_name)
    {
        int iRet = 0;
        eRwType_t rwType = Conn_Write;
        
        DBSConnectionParams dbsMasterConf;
        if( readDbsfile(dbsfile,dbsMasterConf,dbs_master_name) != 0)
        {
            return ERR_DBS_FAIL;
        }  
        
        DBSConnectionParams dbsSlaveConf;
        if( readDbsfile(dbsfile,dbsSlaveConf,dbs_slave_name) != 0)
        {
            return ERR_DBS_FAIL;
        } 
        
        SQLConnectionRw* instance = nullptr;
        try
        {
            instance = SQLConnectionRw::getInstance();
            if(!instance)
            {
                dbslog(DBS_ERROR,"getInstance error!!!");
                return ERR_DBS_FAIL;
            }
        }
        catch(const std::exception& ex)
        {
            dbslog(DBS_ERROR,"getInstance exception %s!!!",ex.what());
            return ERR_DBS_FAIL;
        }  
    
        //create writer connection
        rwType = Conn_Write;
        iRet = instance->NewConnectionRW(
            dbsMasterConf.dbsType,
            dbsMasterConf.dbsConnStr,
            dbsMasterConf.dbsUser,
            dbsMasterConf.dbsPass,
            dbsMasterConf.dbsCharset,
            dbsMasterConf.dbsEncrypt,
            rwType
            ); 
        if(iRet != ERR_DBS_OK)
        {
            dbslog(DBS_WARN,"Creat master connection error");
            return ERR_DBS_FAIL;
        }	
        
        //create reader connection
        rwType = Conn_Read;
        std::vector<string> dbConnStrs = topdbs_string::split(dbsSlaveConf.dbsConnStr,";;");
        if(dbConnStrs.size() == 0)
        {
            dbslog(DBS_WARN,"The slave connStr is NULL");
            return ERR_DBS_OK;
        }	
        
        for(auto& c : dbConnStrs)
        {
            iRet = instance->NewConnectionRW(
                dbsSlaveConf.dbsType,
                c,
                dbsSlaveConf.dbsUser,
                dbsSlaveConf.dbsPass,
                dbsSlaveConf.dbsCharset,
                dbsSlaveConf.dbsEncrypt,
                rwType
                ); 
            if(iRet != ERR_DBS_OK)
            {
                dbslog(DBS_ERROR,"Creat slave connection error");
                continue;
            }	 	
        }
        return ERR_DBS_OK;
    }

    SQLConnectionRw* SQLConnectionRw::getInstance()
    {
        if (SQLConnectionRw::m_instance==nullptr)
        {
            std::lock_guard<std::mutex> _lock(SQLConnectionRw::m_lock);
            if(SQLConnectionRw::m_instance==nullptr)
            {
                SQLConnectionRw::m_instance = new SQLConnectionRw();
            }
        }
        return SQLConnectionRw::m_instance;				
    }


    SQLConnection* SQLConnectionRw::GetConnection(const string& sSQL,bool isForce)
    {
        return SQLConnectionRw::getInstance() 
            ? SQLConnectionRw::getInstance()->GetIdleConnection(sSQL,isForce) 
            : nullptr;
    }

    int SQLConnectionRw::ReleaseConnection(SQLConnection* pConnection)
    {
        return SQLConnectionRw::getInstance() 
            ? SQLConnectionRw::getInstance()->ReleaseIdleConnection(pConnection) 
            : ERR_DBS_FAIL ;
    }  

    int SQLConnectionRw::UnInitEnv()
    {
        if (SQLConnectionRw::m_instance)
        {
            std::lock_guard<std::mutex> _lock(SQLConnectionRw::m_lock);
            if (SQLConnectionRw::m_instance)
            {
                SQLConnectionRw::m_instance->DeleteSQLConnectionRw();
                delete SQLConnectionRw::m_instance;
                SQLConnectionRw::m_instance = nullptr;
            }
        }
        return ERR_DBS_OK;
    }      
}